/* Copyright 2010 John L. Reilly

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

package com.riq;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONArray;
import org.json.JSONObject;

import com.riq.entity.Alert;
import com.riq.entity.Department;
import com.riq.entity.Location;
import com.riq.entity.Member;
import com.riq.entity.Tracking;

public class MailHandlerServlet extends HttpServlet { 

  private static final Logger log = Logger.getLogger(MailHandlerServlet.class.getName());
  private static final long serialVersionUID = 1L;
  private boolean textIsHtml = false;
  private String responseType = "";

  
  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
    doGet(request, response);
  }
  
  @Override
  public void doGet(HttpServletRequest req, 
          HttpServletResponse resp) 
  
  throws IOException {  

    log.info("Inside MailServlet doPost");
  
    // TODO get path information to determine target Department for this email
    
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Properties props = new Properties(); 
    Session session = Session.getDefaultInstance(props, null);

    try {

      MimeMessage msg = new MimeMessage(session, req.getInputStream());
      Address sender = msg.getFrom()[0];
      Address replyTo = msg.getReplyTo()[0];
        
//      DEBUG
//	    log.info("Message Class: " + msg.getClass());
//	    log.info("Message Stream: " + msg.getInputStream());
//	    log.info("Message RawStream: " + msg.getRawInputStream());
//	    log.info("Message Flags: " + msg.getFlags());
//	    log.info("Message Content: " + msg.getContent().toString());
//	    log.info("Message ContentID: " + msg.getContentID());
//	    log.info("Message ContentMD: " + msg.getContentMD5());
//	    log.info("Message ContentType: " + msg.getContentType());
//	    log.info("Message Description: " + msg.getDescription());
//	    log.info("Message Disposition: " + msg.getDisposition());
//	    log.info("Message Encoding: " + msg.getEncoding());
//	    log.info("Message Filename: " + msg.getFileName());
//	    log.info("Message Line Count: " + msg.getLineCount());
//	    log.info("Message ID: " + msg.getMessageID());
//	    log.info("Message Number: " + msg.getMessageNumber());
//	    log.info("Message Size: " + msg.getSize());
//	    log.info("Message Subject: " + msg.getSubject());
//	    log.info("Message RawInputStream: " + msg.getRawInputStream().toString());
//	    log.info("Message ReplyTo: " + replyTo);
//	    log.info("Message Sender: " + sender.toString());
//	    log.info("Request URL TO: " +  getServletContext());      
	    
	    String alertMsgString = getText(msg);
     
      alertMsgString = alertMsgString.replaceAll("Begin forwarded message:","");
      alertMsgString = alertMsgString.replaceAll("Forwarded message","");
      alertMsgString = alertMsgString.replaceAll("Dispatch@co.morris.nj.us","");
      alertMsgString = alertMsgString.replaceAll("Date:",""); 
      alertMsgString = alertMsgString.replaceAll(">",""); 
      alertMsgString = alertMsgString.replaceAll("::","-"); 
      alertMsgString = alertMsgString.replaceAll("< ",""); 
      alertMsgString = alertMsgString.replaceAll("----------",""); 
      alertMsgString = alertMsgString.replaceAll("Subject:",""); 
      alertMsgString = alertMsgString.replaceAll("To:",""); 
      alertMsgString = alertMsgString.replaceAll("From:",""); 

      //  Added to grab gmail confirmations
      //  alertMsgString = alertMsgString.substring(50, 300);

      try {

        String queryDept = "select from " + Department.class.getName() +
        // TODO: johnreilly workaround for getting allRecipients... 
        // " where dispatchId == '" + toAddressDigitsOnly + "' " + 
        " where dispatchId == '2599' " + 
        " && fromEmail == '" + sender + "' ";
        log.info("queryDept: " + queryDept);   
        List<Department> depts = (List<Department>) pm.newQuery(queryDept).execute();
        
        if (depts.size() != 0) {
        
          for (Department d : depts)  {
  
          // need to filter further e.g. last two hours
          String queryAlert = "select from " + Alert.class.getName() +
          " where deptId == " + depts.get(0).getid() +
          " && messageId == '" + msg.getMessageID() + "' "; 
          log.info("queryAlert: " + queryAlert); 
          List<Alert> alerts = (List<Alert>) pm.newQuery(queryAlert).execute();
          log.info("queryAlert Result Qty: " + alerts.size());    
          
            if (alerts.size() == 0) {
                  
              riqGeocode(alertMsgString);
              
              // geocode the address from the Alert
              Map<String, String> geoResults = riqGeocode(alertMsgString);
              log.info("lat print: " + geoResults.get("lat"));
              log.info("lng print: " + geoResults.get("lng"));
              
              // create the alert
              Long deptId = depts.get(0).getid();
              Long timeStamp = System.currentTimeMillis();		
              Alert a = new Alert (
                deptId,
                timeStamp,
                alertMsgString.trim(),
                sender.toString(),
                "Active",
                "",
                geoResults.get("lat"),
                geoResults.get("lng"),
                msg.getMessageID()
                );
              pm.makePersistent(a);
              
              // query to get id of this alert
              String queryThisAlert = "select from " + Alert.class.getName() +
              " where deptId == " + deptId + " " + 
              " && timeStamp == " + timeStamp;
              log.info("queryThisAlert: " + queryThisAlert);
              List<Alert> thisAlert = (List<Alert>) pm.newQuery(queryThisAlert).execute();
              log.info("queryCurrentAlert Result Qty: " + thisAlert.size());   
      
              // create Tracking records for "special" Locations
              String querySpecialLocs = "select from " + Location.class.getName() +
              " where special == 'yes' && deptId == " + deptId ;
              log.info("querySpecialLocs: " + querySpecialLocs);
              List<Location> specialLocs = (List<Location>) pm.newQuery(querySpecialLocs).execute();
              log.info("querySpecialLocs qty: " + specialLocs.size());
    
              for (Location specL : specialLocs) {
                Tracking tSL = new Tracking (
                        deptId,
                        thisAlert.get(0).getid(),
                        specL.getid(),
                        System.currentTimeMillis(),
                        null,  // TODO: johnreilly authorId tbd
                        "location",
                        specL.getid(),
                        null,
                        "autoAddLocation"
                        );
                      pm.makePersistent(tSL);
              }
            
              // search Dept for Location where distant Members presumed not to be responding are set
              String queryFarawayLocs = "select from " + Location.class.getName() +
              " where vru == '2' && deptId == " + deptId;
              log.info("querySpecialLocs: " + queryFarawayLocs);
              List<Location> farawayLoc = (List<Location>) pm.newQuery(queryFarawayLocs).execute();
              log.info("queryFarawayLocs qty: " + farawayLoc.size());
              
              // create Tracking records for Members presumed not to be responder due to fresh ETA
              String queryFarAwayResponders = "select from " + Member.class.getName() +
              " where distGroup == '4' " ;
              log.info("queryFarAwayResponders: " + queryFarAwayResponders);
              List<Member> farawayMembers = (List<Member>) pm.newQuery(queryFarAwayResponders).execute();
              log.info("queryFarAwayResponders qty: " + farawayMembers.size());
              
                if (farawayMembers.size() != 0) {
        	      for (Member far : farawayMembers) {
        	        Tracking tMFAR = new Tracking (
        	                deptId,
        	                thisAlert.get(0).getid(),
        	                farawayLoc.get(0).getid(),
        	                System.currentTimeMillis(),
        	                null,  // TODO: johnreilly authorId tbd
        	                "farawayAddMember",
        	                far.getid(),
        	                null,
        	                responseType
        	                );
        	              pm.makePersistent(tMFAR);
        	        }
                }
            
            
              // TODO: testing restriction HACKER - remove at launch
              // send alert email
              String queryMember = "select from " + Member.class.getName() +
              " where deptId == " + deptId + " && type == 'Hacker' "; 
              List<Member> members = (List<Member>) pm.newQuery(queryMember).execute();	
      
              //TODO Geocode address to insure easy navigation
              //TODO johnreilly: add street and town fields to Alert
              msg.setFrom(new InternetAddress("admin@responderiq05.appspotmail.com", 
                  "FirstResponder"));
              msg.setSubject("Alert!" );
              msg.setText(" Y=" + d.getinboundVRU1() + 
              		        "&N=" + d.getinboundVRU2() + 
                          // "&3=" + d.getinboundVRU3() +  
              		        "&A=" + thisAlert.get(0).getalertMsgString());
              		
              // send message to all dept members who permit tracking (and sending email alerts)
                for (Member m: members) {
                  msg.addRecipient(Message.RecipientType.TO, 
                          new InternetAddress(m.getsmsAddress(), m.getfirstName() + "" + 
                              m.getlastName())); 
                  log.info("Member: " + m.getsmsAddress() + "|" + m.getlastName());
              }
            }
          // only process for one department - should only be one department
          break;
          }
        }
      } catch (MessagingException me) {
        log.info("Error: Messaging");
      } catch (IndexOutOfBoundsException mi) {
        log.info("Error: Out of Bounds");
      }

      // Do not send alert notification if email is a Google Confirmation
//      if (! sender.toString().contains("mail-noreply@google.com")) {
//        Transport.send(msg);
//      }
      
    } catch (MessagingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();

    } finally { pm.close();  }

  }
  
  
  
  private Map<String, String> riqGeocode(String alertMsgString) {
    
      Map<String, String> latlngmap = new HashMap();
  
      String alertStreet = alertMsgString.substring(0, alertMsgString.indexOf("[")).trim();
             alertStreet = alertStreet.replaceAll(" ","+");
             log.info("inside riqGeocode street: " + alertStreet);
    
      String alertTown = alertMsgString.substring(
             alertMsgString.indexOf("[")+1, alertMsgString.indexOf("]")).trim();
             alertTown = alertTown.replaceAll(" ","+");
             log.info("inside riqGeocode town: " + alertTown);
      
      // TODO johnreilly: get from dept record
      String alertState = "NJ"; 
      
      // TODO johnreilly: added region bias once geocode is in Alert record
      String geocodeURL = "http://maps.googleapis.com/maps/api/geocode/json?address=" + 
             alertStreet + ",+" + alertTown + ",+" + alertState + "&sensor=false";
          
      try {
        
        URL url;
        String lat = "";
        String lng = "";
        url = new URL(geocodeURL);
        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
        StringBuilder tempString = new StringBuilder();
        String line;
        
        while ((line=reader.readLine())!=null) {
          tempString.append(line);
        }
        
        JSONObject obj = new JSONObject(tempString.toString());
        
        if ("ok".equalsIgnoreCase(obj.getString("status"))) {
          JSONArray results = obj.getJSONArray("results");
          if (results.length()>0) {
            JSONObject result = results.getJSONObject(0);
            JSONObject location = result.getJSONObject("geometry").getJSONObject("location");
            lat = String.valueOf(location.getDouble("lat"));
            lng = String.valueOf(location.getDouble("lng"));
            log.info("Lat: " + lat);
            log.info("Lng: " + lng);  
            
            latlngmap.put("lat", lat);
            latlngmap.put("lng", lng);
          }       
        }
        
  
  //      latlngmap.put("street", alertStreet);
  //      latlngmap.put("town", alertTown);
  //      latlngmap.put("state", alertState);
          
        } catch (Exception e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
  
        return (latlngmap);
    }
  
  
  private String getText(Part p) throws
  MessagingException, IOException {
//    log.info("Message S Zero");
//    log.info("Inside MailServlet getText");
  
    if (p.isMimeType("text/*")) {
      String s = (String)p.getContent();
      log.info("Message S1: " + s);
      boolean textIsHtml = p.isMimeType("text/html");
      return s;
    }
  
    if (p.isMimeType("multipart/alternative")) {
      Multipart mp = (Multipart)p.getContent();
      String text = null;
      for (int i = 0; i < mp.getCount(); i++) {
        Part bp = mp.getBodyPart(i);
        if (bp.isMimeType("text/plain")) {
          if (text == null)
            text = getText(bp);
          log.info("Message S2: " + text);
          continue;
        } else if (bp.isMimeType("text/html")) {
          String s = getText(bp);
          if (s != null)
            log.info("Message S3: " + s);
          return s;
        } else if (bp.isMimeType("message/rfc822")) {
          Object nestedObject = "";   
          nestedObject = bp.getContent();
          Multipart nestedPart = (Multipart)nestedObject;
          BodyPart nestedBodyPart = nestedPart.getBodyPart(0);
          String s = getText(nestedBodyPart); 
          if (s != null)
            log.info("Message Nested: " + s);
          return s;
        } else {
          log.info("Message nada");
          return getText(bp);
        }
      }
      return text;
  
    } else if (p.isMimeType("multipart/*")) {
      Multipart mp = (Multipart)p.getContent();
      for (int i = 0; i < mp.getCount(); i++) {
        String s = getText(mp.getBodyPart(i));
        if (s != null) {
          log.info("Message S4: " + s);
          return s;
        }
      }
    }
    return null;
  }
  
}






